#ifndef __MINIUTIL_FRAME_STORAGESERVICE_H__
#define __MINIUTIL_FRAME_STORAGESERVICE_H__

#include "miniutil/scope.h"
#include "miniutil/address.h"
#include "miniutil/lenmsgreader.h"
#include "miniutil/socketmultiplex.h"
#include "miniutil/threadservice.h"

/*
	What is Storage Service?
	1, it have native API directly control local file
	2, or it can post request to local agent which help it to control local file
	3, or it can post request to remote agent which help it to control remote file
	
	It just have 4 API, read/write/existinfo/setlength
	user can get/set the file part, can get part info by existinfo
	filename can be following:
		abc/cde.wmv
		zbc.ktv@192.168.1.1:8812
		abc/cde.wmv@202.112.58.200:1112

	user should set the file length when length information available
	when file is not downloaded fully, the file will have suffix .part, when it was finished, the suffix is removed!

*/
namespace miniutil { namespace frame{

	typedef miniutil::m_int64_t fs_t;
		
	class RemoteFileData
	{
		public:
			RemoteFileData(std::string path);
			int Read(int sessid, fs_t offset, char* buf, unsigned int size);
			int Write(fs_t offset, char* buf, unsigned int size);
			int ExistInfo(std::list<std::pair<fs_t, fs_t> >&);
		public:
			struct CacheData
			{
				CacheData();
				~CacheData();
				
				char *pdata;
				fs_t offset;
				unsigned int len;
			};
			
			std::vector<CacheData*> mwritecache; 
			//std::vector<CacheData*> mreadcache; //some recently cache
			std::map<int, CacheData*> mreadcache;
				
			std::string mpath;			
			
			unsigned int updatesesid;
			std::list<std::pair<fs_t, fs_t> > mexistinfo;
	};
	
	class RemoteFsNode
	{
		public:
			RemoteFsNode(miniutil::multinet::SocketMulplexService *);
			int Open(std::string, int timeout);			
			int AsyncRead(std::string, fs_t offset, unsigned int size);
			int AsyncWrite(std::string, char* buf, fs_t offset, unsigned int size);
			int AsyncExistInfo(std::string);

			int Working(); //flush write data, check read data
		public:
			unsigned int CreateSessionId();	
			void AddInvalidSession(unsigned int);
			int CheckSessionStus(unsigned int/*, unsigned int tmoutms*/);  //-1 timeout 0 ok 1 fail
			std::map<std::string, RemoteFileData*> mallcache;
		private:
			std::map<unsigned int, unsigned int> msessionstus; //ok, fail
			std::set<unsigned int> minvalidsess;
			SOCKET msoc;
			miniutil::soaddr remoteaddr;
			miniutil::multinet::SocketMulplexService *mpss;
			int mstus;//fail, conecting, connected

			//void CheckProxyStatus();
			unsigned int presessionid;
			time_t mlstoprsec;
			miniutil::mutex mtx;
		private:
			int HandleReadResp(char* pbuf, int len);
			int HandleWriteResp(char* pbuf, int len);
			int HandleStusResp(char* pbuf, int len);
			
			miniutil::multinet::LenMsgReader mmsgreader;
	};
	
	class RemoteProxy
	{
		public:
			RemoteProxy();
			int SetMpss(miniutil::multinet::SocketMulplexService *pmss);
			std::map<std::string, RemoteFsNode*> allremotes; //<ip, filenode>
			int Working();
			
			int Read(std::string, char* buf, fs_t offset, int size); //path include @ip:port
			int Write(std::string, char* buf, fs_t offset, int size);
			int ExistInfo(std::string, std::list<std::pair<fs_t, fs_t> >&);
		private:
			miniutil::mutex mtx;
			RemoteFsNode* TryFindFsNode(std::string);
			miniutil::multinet::SocketMulplexService *pmss;
	};
	
	//-----------------------------------
	class LocalSharedFile   //slow..
	{
		public:
			LocalSharedFile();
			~LocalSharedFile();
			int Open(std::string, std::string, std::string);
			int Close();
			int Read(char* buf, fs_t offset, int size);
			//int Seek(fs_t offset);
			int Write(char* buf, fs_t offset, int size);
			int ExistInfo(std::list<std::pair<fs_t, fs_t> >&);
			int Remove();
			fs_t GetLength();
			int SetLength(fs_t len);
            bool IsComplete();
		protected:
			FILE* TryOpenOneFile(std::string, std::string option="");

			miniutil::mutex mtx;
			FILE* mpdatafile;
			FILE* mpmetafile;
			std::string mfilepath; //abs. path
			std::string mmetapath;
			
			//std::list<std::pair<fs_t, fs_t> > mexitinfo;
			miniutil::util::Scope<fs_t> mexitinfo;
			fs_t mlength;
			unsigned int updatesesid;

			int Lock();
			int UnLock();
			int FlushMetaInfo();
			int InnerOpen();
			int InnerClose();
			
	};

	class LocalFile
	{
		public:
			LocalFile();
			~LocalFile();
			int Open(std::string, std::string, std::string);
			int Close();
			int Read(char* buf, fs_t offset, int size);
			//int Seek(fs_t offset);
			int Write(char* buf, fs_t offset, int size);
			int ExistInfo(std::list<std::pair<fs_t, fs_t> >&);
			int Remove();
			fs_t GetLength();
			int SetLength(fs_t len);
            bool IsComplete();
		protected:
			FILE* TryOpenOneFile(std::string, std::string option="");

			miniutil::mutex mtx;
			FILE* mpdatafile;
			FILE* mpmetafile;
			std::string mfilepath; //abs. path
			std::string mmetapath;
			
			//std::list<std::pair<fs_t, fs_t> > mexitinfo;
			miniutil::util::Scope<fs_t> mexitinfo;
			fs_t mlength;
			unsigned int updatesesid;

			int FlushMetaInfo();
			int InnerOpen();
			int InnerClose();
			
	};
	class LocalFileSystem
	{
		public:
			static LocalFileSystem* GetInstance(std::string root="");
		public:
			LocalFile* GetFile(std::string, std::string option="r");
			~LocalFileSystem();
			//int RemoveFile(std::string);
		protected:
			static LocalFileSystem* pinstance;
			LocalFileSystem();
			std::string mlocalroot;
			//std::map<std::string, LocalFile*> mallfilecache;
	};
	
	//-----------------------------------------------
	
	class LocalStub
	{
		public:
			LocalStub();
			int Open(unsigned short port);
			int Close();
			int Working();
			int SetMpss(miniutil::multinet::SocketMulplexService *pmss);

			struct LocalStubNode
			{
				miniutil::soaddr incomingaddr;
				SOCKET msoc;
				miniutil::multinet::LenMsgReader mmsgreader;
			};

			int HandleReadReq(LocalStubNode* pnode, char* pbuf, unsigned int len);
			int HandleWriteReq(LocalStubNode* pnode, char* pbuf, unsigned int len);
			int HandleStusReq(LocalStubNode* pnode, char* pbuf, unsigned int len);
			
			std::vector<LocalStubNode> vallpeers;
				
		private:
			unsigned short port;
			LocalFileSystem* plocalfs;
			SOCKET listensoc;
			miniutil::multinet::SocketMulplexService *pmss;

	};
	
	//--------------------------------------------------
	
		/*
	abc/cde.wmv
	zbc.ktv@192.168.1.1:8812
	abc/cde.wmv@202.112.58.200:1112
	
	*/
	
	class StorageService:public ThreadService
	{
		public:
			const static int MODE_REMOTE_CLIENT = 1;
			const static int MODE_SERVER = 2;
			
			StorageService();
			int SetMode(int mode, unsigned short port = 0);
			virtual int BeforeStart();
			virtual int WorkingTask();
			virtual int AfterStop();

			int SetRemoteSync(bool);
			int SetLocalRoot(std::string path);
				
			//API
			int Read(std::string filename, char* buf, fs_t offset, fs_t size, unsigned short timeout = 0);
			int Write(std::string filename, char* buf, fs_t offset, fs_t size, unsigned short timeout = 0);
			int ExistInfo(std::string filename, std::list<std::pair<fs_t, fs_t> >&);

			int Remove(std::string path);
			int SetLength(std::string path, fs_t length);
			fs_t GetLength(std::string path);
            int IsComplete(std::string path);
		private:
			miniutil::multinet::SocketMulplexService_Select msmss;
			RemoteProxy mremoteproxy;
			LocalStub mlocalstub;
			LocalFileSystem* mplocalfs;
			unsigned short mlistenport;
			int mmode;
	};
	
} }	

#endif
